﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using sscore;
using DM4;

namespace DMN_MailListener
{
    internal class Worker : IDisposable
    {
        public readonly Dictionary<DateTime, Task> Tasks;
        private CancellationTokenSource _cts;

        private readonly LogHandler _writeLog;
        private readonly WorkerResultHandler _workerResultHandler;
        private readonly WorkerResult _workerResult;
        private readonly string _connectionString;


        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
                if (_cts != null)
                {
                    _cts.Dispose();
                    _cts = null;
                }
        }
        ~Worker()
        {
            Dispose(false);
        }

        public Worker(LogHandler writeLog, WorkerResultHandler workerResultHandler, string connectionString)
        {
            _writeLog = writeLog;
            _workerResultHandler = workerResultHandler;
            _cts = new CancellationTokenSource();
            _connectionString = connectionString;

            _workerResult = new WorkerResult();
            Tasks = new Dictionary<DateTime, Task>();
        }

        public void Start(MailListenerParameters mailParam)
        {
            Start(new List<MailListenerParameters> { mailParam });
        }

        public void Start(List<MailListenerParameters> mailParams)
        {
            //Создаем отдельный таск для вставки в базу параметров проверки почтового ящика
            //В этом таске, после вставки в базу, вызовем функцию проверки почтового ящика
            Action<object> action = CreateMailDbParams;
            CreateMailParamsTaskParams cmtp = new CreateMailParamsTaskParams() { Ct = _cts.Token, MlParams = mailParams, MailCheck = Check };
            Task task = new Task(action, cmtp, _cts.Token);
            task.Start();
        }

        private void CreateMailDbParams(object prm)
        {
            CreateMailParamsTaskParams cmtp = (CreateMailParamsTaskParams)prm;
            WorkWithDbData db = new WorkWithDbData(_connectionString, _writeLog);
            List<MailListenerWorkerParameters> mlwp = db.CreateMailListenerWorkerParameters(cmtp.MlParams);
            if (cmtp.MailCheck != null)
                cmtp.MailCheck(mlwp);
        }

        public void Check(List<MailListenerWorkerParameters> mailParams)
        {
            if (mailParams != null && mailParams.Count != 0)
            {
                WriteLog(String.Format("Start check mail, letters count [{0}]", mailParams.Count), MessageType.Cool);
                
                foreach (MailListenerWorkerParameters mp in mailParams) //для проверки каждого почтового ящика стартуем отдельный таск
                {
                    if (_cts.IsCancellationRequested) break;

                    Thread.Sleep(10);
                    TaskParams tprm = new TaskParams { Id = DateTime.Now, Ct = _cts.Token, Log = _writeLog, ConnectionString = _connectionString, MailParams = mp };
                    tprm.MailChecker = new MailChecker(OnOneTaskResult, tprm);
                    CreateAndStartTask(tprm);
                }
            }
            else
            {
                WriteLog("No mail parameters found for check.", MessageType.Cool);
                SetWorkerResult();
            }
        }
        private void CreateAndStartTask(TaskParams tprm)
        {  
            Action<object> action = StartOneTask;

            if (Tasks.ContainsKey(tprm.Id))
                Tasks.Remove(tprm.Id);

            Tasks.Add(tprm.Id, new Task(action, tprm, _cts.Token));
            Tasks[tprm.Id].Start();
        }

        private void StartOneTask(object prm)
        {
            TaskParams tPrm = (TaskParams)prm;
            CancellationToken ct = tPrm.Ct;

            ct.ThrowIfCancellationRequested();
            
            if (!ct.IsCancellationRequested)                
                tPrm.MailChecker.DoWork();
        }

        private void OnOneTaskResult(TaskParams tprm)
        {
            _workerResult.CompleteCount++;

            if (Tasks.ContainsKey(tprm.Id))
                Tasks.Remove(tprm.Id);

            if (Tasks.Count == 0)
                SetWorkerResult();

            SetWorkerResult();
        }

        public void Stop()
        {
            _cts.Cancel();
            Tasks.Clear();
            WriteLog("MailCheck worker is stopped.", MessageType.Error);
        }


        private void SetWorkerResult()
        {
            if (_workerResultHandler != null)
                _workerResultHandler(_workerResult);
        }

        private void WriteLog(string aMessage, MessageType type = MessageType.Info)
        {
            if (_writeLog != null)
                _writeLog(aMessage, type);
        }
    }
}
